/*
 * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package de.slothsoft.themes.black;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;
import javax.swing.border.Border;

/**
 * LoweredBorder - A recessed rounded inner shadowed border. Used as the
 * standard Black TitledBorder. This class is both a painter and a swing
 * border.
 * 
 * @author Jasper Potts
 */
class LoweredBorder extends AbstractRegionPainter implements Border {
	private static final int IMG_SIZE = 30;
	private static final int RADIUS = 13;
	private static final Insets INSETS = new Insets(10, 10, 10, 10);
	private static final PaintContext PAINT_CONTEXT = new PaintContext(INSETS, new Dimension(IMG_SIZE, IMG_SIZE),
			false, PaintContext.CacheMode.NINE_SQUARE_SCALE, Integer.MAX_VALUE, Integer.MAX_VALUE);

	// =========================================================================
	// Painter Methods

	@Override
	protected Object[] getExtendedCacheKeys(JComponent c) {
		return (c != null) ? new Object[] { c.getBackground() } : null;
	}

	/**
	 * Actually performs the painting operation. Subclasses must implement this
	 * method. The graphics object passed may represent the actual surface being
	 * rendererd to, or it may be an intermediate buffer. It has also been
	 * pre-translated. Simply render the component as if it were located at 0, 0
	 * and had a width of <code>width</code> and a height of <code>height</code>. For
	 * performance reasons, you may want to read the
	 * clip from the Graphics2D object and only render within that space.
	 * 
	 * @param g The Graphics2D surface to paint to
	 * @param c The JComponent related to the drawing event. For example,
	 *            if the region being rendered is Button, then <code>c</code> will be a
	 *            JButton. If the region being drawn is
	 *            ScrollBarSlider, then the component will be JScrollBar.
	 *            This value may be null.
	 * @param width The width of the region to paint. Note that in the case of
	 *            painting the foreground, this value may differ from
	 *            c.getWidth().
	 * @param height The height of the region to paint. Note that in the case of
	 *            painting the foreground, this value may differ from
	 *            c.getHeight().
	 */
	@Override
	protected void doPaint(Graphics2D g, JComponent c, int width, int height, Object[] extendedCacheKeys) {
		Color color = Color.WHITE;
		BufferedImage img1 = new BufferedImage(IMG_SIZE, IMG_SIZE, BufferedImage.TYPE_INT_ARGB);
		BufferedImage img2 = new BufferedImage(IMG_SIZE, IMG_SIZE, BufferedImage.TYPE_INT_ARGB);
		// draw shadow shape
		Graphics2D g2 = (Graphics2D) img1.getGraphics();
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setColor(color);
		g2.fillRoundRect(2, 0, 26, 26, RADIUS, RADIUS);
		g2.dispose();
		// draw shadow
		InnerShadowEffect effect = new InnerShadowEffect();
		effect.setDistance(1);
		effect.setSize(3);
		effect.setColor(getLighter(color, 2.1f));
		effect.setAngle(90);
		effect.applyEffect(img1, img2, IMG_SIZE, IMG_SIZE);
		// draw outline to img2
		g2 = (Graphics2D) img2.getGraphics();
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setClip(0, 28, IMG_SIZE, 1);
		g2.setColor(getLighter(color, 0.90f));
		g2.drawRoundRect(2, 1, 25, 25, RADIUS, RADIUS);
		g2.dispose();
		// draw final image
		if (width != IMG_SIZE || height != IMG_SIZE) {
			ImageScalingHelper.paint(g, 0, 0, width, height, img2, INSETS, INSETS,
					ImageScalingHelper.PaintType.PAINT9_STRETCH, ImageScalingHelper.PAINT_ALL);
		} else {
			g.drawImage(img2, 0, 0, c);
		}
		img1 = null;
		img2 = null;
	}

	/**
	 * <p>
	 * Gets the PaintContext for this painting operation. This method is called on every
	 * paint, and so should be fast and produce no garbage. The PaintContext contains
	 * information such as cache hints. It also contains data necessary for decoding
	 * points at runtime, such as the stretching insets, the canvas size at which the
	 * encoded points were defined, and whether the stretching insets are inverted.
	 * </p>
	 * <p/>
	 * <p>
	 * This method allows for subclasses to package the painting of different states with
	 * possibly different canvas sizes, etc, into one AbstractRegionPainter
	 * implementation.
	 * </p>
	 * 
	 * @return a PaintContext associated with this paint operation.
	 */
	@Override
	protected PaintContext getPaintContext() {
		return PAINT_CONTEXT;
	}

	// =========================================================================
	// Border Methods

	/**
	 * Returns the insets of the border.
	 * 
	 * @param c the component for which this border insets value applies
	 */
	@Override
	public Insets getBorderInsets(Component c) {
		return (Insets) INSETS.clone();
	}

	/**
	 * Returns whether or not the border is opaque. If the border is opaque, it
	 * is responsible for filling in it's own background when painting.
	 */
	@Override
	public boolean isBorderOpaque() {
		return false;
	}

	/**
	 * Paints the border for the specified component with the specified position
	 * and size.
	 * 
	 * @param c the component for which this border is being painted
	 * @param g the paint graphics
	 * @param x the x position of the painted border
	 * @param y the y position of the painted border
	 * @param width the width of the painted border
	 * @param height the height of the painted border
	 */
	@Override
	public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
		JComponent comp = (c instanceof JComponent) ? (JComponent) c : null;
		if (g instanceof Graphics2D) {
			Graphics2D g2 = (Graphics2D) g;
			g2.translate(x, y);
			paint(g2, comp, width, height);
			g2.translate(-x, -y);
		} else {
			BufferedImage img = new BufferedImage(IMG_SIZE, IMG_SIZE, BufferedImage.TYPE_INT_ARGB);
			Graphics2D g2 = (Graphics2D) img.getGraphics();
			paint(g2, comp, width, height);
			g2.dispose();
			ImageScalingHelper.paint(g, x, y, width, height, img, INSETS, INSETS,
					ImageScalingHelper.PaintType.PAINT9_STRETCH, ImageScalingHelper.PAINT_ALL);
		}
	}

	private Color getLighter(Color c, float factor) {
		return new Color(Math.min((int) (c.getRed() / factor), 255), Math.min((int) (c.getGreen() / factor), 255),
				Math.min((int) (c.getBlue() / factor), 255));
	}
}
